#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <string.h>
#include <strings.h>

#include "psd_radix.h"
#include "radix.h"
#include "libxsp.h"

static int psd_prepare_addrmask(const char *addr_mask, char *output_buf, int buf_size);

int psd_radix_add(radix_tree_t *route_tree, const char *addr_mask, const void *value) {
	radix_node_t *node;
	prefix_t *prefix;
	char buf[60];

	if (psd_prepare_addrmask(addr_mask, buf, 60) != 0) {
		goto error_exit;
	}

	prefix = ascii2prefix(0, buf);
	if (!prefix) {
		goto error_exit;
	}

	node = radix_lookup(route_tree, prefix);
	if (!node) {
		goto error_exit2;
	}

	node->data = (void *) value;

	Deref_Prefix(prefix);

	return 0;

error_exit2:
	Deref_Prefix(prefix);
error_exit:
	return -1;

}

void *psd_radix_remove(radix_tree_t *route_tree, const char *addr_mask) {
	radix_node_t *node;
	prefix_t *prefix;
	char buf[60];
	void *retval;

	if (psd_prepare_addrmask(addr_mask, buf, 60) != 0) {
		goto error_exit;
	}

	prefix = ascii2prefix(0, buf);
	if (!prefix) {
		goto error_exit;
	}

	node = radix_search_exact(route_tree, prefix);
	if (!node)
		goto error_exit2;

	retval = node->data;

	radix_remove(route_tree, node);

	Deref_Prefix(prefix);

	return 0;

error_exit2:
	Deref_Prefix(prefix);
error_exit:
	return NULL;
}

void *psd_radix_lookup(radix_tree_t *route_tree, const char *hop_id) {
	prefix_t prefix;
	radix_node_t *node;
	void *retval;
	struct addrinfo *hop_addr;
	struct addrinfo *curr_addr;

	hop_addr = xsp_lookuphop(hop_id);
	if (!hop_addr)
		return NULL;

	for(curr_addr = hop_addr; curr_addr != NULL; curr_addr = curr_addr->ai_next) {
		prefix.bitlen = 32;
		prefix.family = curr_addr->ai_family;
		if (curr_addr->ai_family == AF_INET)
			prefix.add.sin = ((struct sockaddr_in *)curr_addr->ai_addr)->sin_addr;
#ifdef HAVE_IPV6
		else if (curr_addr->ai_family == AF_INET6)
			prefix.add.sin6 = ((struct sockaddr_in6 *)curr_addr->ai_addr)->sin6_addr;
#endif
		else
			continue;

		if ((node = radix_search_best(route_tree, &prefix)) != NULL) {
			retval = node->data;
			break;
		}
	}
	
	if (curr_addr == NULL)
		retval = NULL;

	freeaddrinfo(hop_addr);

	return retval;
}

static int psd_prepare_addrmask(const char *addr_mask, char *output_buf, int buf_size) {
	char *am_dup;
	char *tmp;
	char *tmp_p = NULL;
	char *addr;
	char *ip;
	int netmask;
	struct hostent *he;

	am_dup = strdup(addr_mask);
	if (!am_dup)
		goto error_exit;

	tmp = strtok_r(am_dup, "/", &tmp_p);
	if (!tmp)
		goto error_exit2;

	addr = strdup(tmp);

	tmp = strtok_r(NULL, "/", &tmp_p);
	if (!tmp)
		goto error_exit3;

	netmask = atoi(tmp);

	if (netmask < 0 || netmask > 32)
		goto error_exit3;

	// XXX: this should use getaddrinfo or something
	he = gethostbyname(addr);
	if (!he)
		goto error_exit3;

	tmp = inet_ntoa(*(struct in_addr *)he->h_addr_list[0]);
	if (!tmp)
		goto error_exit4;

	snprintf(output_buf, buf_size, "%s/%d", tmp, netmask);

	free(addr);
	free(am_dup);

	return 0;

error_exit4:
error_exit3:
	free(addr);
error_exit2:
	free(am_dup);
error_exit:
	return -1;
}


